package com.wiscamp.ninechapters.panels.infrastructure.repositories;


import com.wiscamp.ninechapters.panels.domain.entities.Role;
import com.wiscamp.ninechapters.panels.domain.repositories.RoleRepository;
import com.wiscamp.ninechapters.panels.infrastructure.converters.RoleConverter;
import com.wiscamp.ninechapters.panels.infrastructure.mappers.RoleMapper;
import com.wiscamp.ninechapters.panels.infrastructure.mappers.RoleMenuMapper;
import com.wiscamp.ninechapters.panels.infrastructure.mappers.RolePermissionMapper;
import com.wiscamp.ninechapters.panels.infrastructure.mappers.RoleViewMapper;
import com.wiscamp.ninechapters.panels.infrastructure.po.RoleMenuPO;
import com.wiscamp.ninechapters.panels.infrastructure.po.RolePO;
import com.wiscamp.ninechapters.panels.infrastructure.po.RolePermissionPO;
import com.wiscamp.ninechapters.panels.infrastructure.views.RoleView;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.common.query.PagedObjects;
import cube.ddd.common.query.PagedQuery;
import cube.ddd.utils.PersistenceUtils;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;

@Repository
public class RoleRepositoryImpl implements RoleRepository {

    private final RoleMapper roleMapper;

    private final RoleViewMapper roleViewMapper;

    private final RoleMenuMapper roleMenuMapper;

    private final RolePermissionMapper rolePermissionMapper;

    public RoleRepositoryImpl(RoleMapper roleMapper, RoleViewMapper roleViewMapper, RoleMenuMapper roleMenuMapper, RolePermissionMapper rolePermissionMapper) {
        this.roleMapper = roleMapper;
        this.roleViewMapper = roleViewMapper;
        this.roleMenuMapper = roleMenuMapper;
        this.rolePermissionMapper = rolePermissionMapper;
    }

    private void loadDetails(Role role) {
        if (Objects.nonNull(role)) {
            var menuIds = roleMenuMapper.findMenuIdsByRole(role.getRoleId());
            var permissionIds = rolePermissionMapper.findPermissionIdsByRole(role.getRoleId());
            role.setMenuIds(menuIds);
            role.setPermissionIds(permissionIds);
        }
    }

    private RolePO getRolePO(long roleId) {
        return PersistenceUtils.findById(roleId, roleMapper::findById);
    }

    @HandleDataException
    public Role getRole(long roleId) {
        var role = PersistenceUtils.findEntityById(roleId, roleViewMapper::findById,
                RoleConverter.Instance::toRoleByView);
        loadDetails(role);
        return role;
    }

    @HandleDataException
    public Role getRoleByCode(long appId, String roleCode) {
        var role = PersistenceUtils.getEntityByIdAndText(appId, roleCode, roleViewMapper::findByRoleCode,
                RoleConverter.Instance::toRoleByView);
        loadDetails(role);
        return role;
    }

    @HandleDataException
    public Collection<Role> getRolesByName(long appId, String roleName) {
        var roles = PersistenceUtils.getEntitiesByIdAndText(appId, roleName, roleViewMapper::findListByRoleName,
                RoleConverter.Instance::toRoleByView);
        for (var role : roles) {
            loadDetails(role);
        }
        return roles;
    }

    @HandleDataException
    public Collection<Role> getRolesByIds(Collection<Long> roleIds) {
        var roles = PersistenceUtils.getEntitiesByIds(roleIds, roleViewMapper::findAllById,
                RoleConverter.Instance::toRoleByView);
        for (var role : roles) {
            loadDetails(role);
        }
        return roles;
    }

    @HandleDataException
    public Collection<Role> getAvailableRoles(long appId) {
        return PersistenceUtils.getEntitiesById(appId, roleViewMapper::findListByAvailable, RoleConverter.Instance::toRoleByView);
    }

    @HandleDataException
    public PagedObjects<Role> getRolesByQuery(PagedQuery query) {
        PagedObjects<RoleView> pagedObjects = PersistenceUtils.getPersistenceObjectsByQuery(query, roleViewMapper::findAll);
        return PersistenceUtils.convertPagedObjects(pagedObjects, RoleConverter.Instance::toRoleByView);
    }

    @HandleDataException
    public Role saveRole(Role role) {
        if (role.getRoleId() <= 0) {
            return PersistenceUtils.createEntity(role, roleMapper::saveAndFlush,
                    RoleConverter.Instance::toRolePO, RoleConverter.Instance::toRole);
        } else {
            var existedPO = getRolePO(role.getRoleId());
            if (existedPO == null) existedPO = new RolePO();
            return PersistenceUtils.updateEntity(role, existedPO, roleMapper::saveAndFlush,
                    RoleConverter.Instance::toRolePO, RoleConverter.Instance::toRole);
        }
    }

    @HandleDataException
    public boolean bindRoleMenu(long roleId, long menuId) {
        var roleMenus = roleMenuMapper.findByRoleAndMenu(roleId, menuId);
        if (roleMenus == null) {
            roleMenus = new RoleMenuPO();
            roleMenus.setRoleId(roleId);
            roleMenus.setMenuId(menuId);
            roleMenuMapper.saveAndFlush(roleMenus);
        }
        return true;
    }

    @HandleDataException
    public boolean unbindRoleMenu(long roleId, long menuId) {
        var roleMenus = roleMenuMapper.findByRoleAndMenu(roleId, menuId);
        if (roleMenus != null) {
            roleMenuMapper.delete(roleMenus);
        }
        return true;
    }

    @HandleDataException
    public boolean bindRolePermission(long roleId, long permissionId) {
        var rolePermissions = rolePermissionMapper.findByRoleAndPermission(roleId, permissionId);
        if (rolePermissions == null) {
            rolePermissions = new RolePermissionPO();
            rolePermissions.setRoleId(roleId);
            rolePermissions.setPermissionId(permissionId);
            rolePermissionMapper.saveAndFlush(rolePermissions);
        }
        return true;
    }

    @HandleDataException
    public boolean unbindRolePermission(long roleId, long permissionId) {
        var rolePermissions = rolePermissionMapper.findByRoleAndPermission(roleId, permissionId);
        if (rolePermissions != null) {
            rolePermissionMapper.delete(rolePermissions);
        }
        return true;
    }
}
